using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using Sinacor.Infra.Service.Validation;
using Sinacor.Infra.Service.Common;
using Sinacor.Infra.Service.Security.Authorization;
using Sinacor.Service.Infra.AuthorizationService.BusinessEntity;
using Sinacor.Infra.Common.Exceptions;

namespace Sinacor.Service.Infra.AuthorizationService.BusinessLogic
{
    /// <summary>
    /// Business Logic BehaviorPolicy
    /// </summary>
    public class BehaviorPolicy : BaseBusinessLogic
    {
        #region Propriedades
        private BusinessLogic.Policy _policyBL = new Policy();
        #endregion

        #region Construtor
        public BehaviorPolicy()
            : base()
        {

        }
        #endregion

        #region Métodos

        /// <summary>
        /// Método InsertBehaviorPolicy
        /// </summary>
        public void InsertBehaviorPolicy(BusinessEntity.BehaviorPolicy request)
        {
            if (request == null)
                throw new ValidationException(929);

            SinacorValidation validation = new SinacorValidation();

            validation.AddValidation<Sinacor.Service.Infra.AuthorizationService.BusinessEntity.BehaviorPolicy>(request, RuleSetConstants.INSERT);

            validation.Validate();

            validation = null;

            ResourceAccess.BehaviorPolicy behaviorPolicyRA = new ResourceAccess.BehaviorPolicy(request.CompanyId.Value);

            behaviorPolicyRA.InsertBehaviorPolicy(request);

        }

        /// <summary>
        /// Método UpdateBehaviorPolicy
        /// </summary>
        public void UpdateBehaviorPolicy(BusinessEntity.BehaviorPolicy request)
        {
            if (request == null)
                throw new ValidationException(930);

            SinacorValidation validation = new SinacorValidation();

            validation.AddValidation<Sinacor.Service.Infra.AuthorizationService.BusinessEntity.BehaviorPolicy>(request, RuleSetConstants.UPDATE);

            validation.Validate();

            validation = null;

            ResourceAccess.BehaviorPolicy behaviorPolicyRA = new ResourceAccess.BehaviorPolicy(request.CompanyId.Value);

            behaviorPolicyRA.UpdateBehaviorPolicy(request);
        }

        /// <summary>
        /// Método DeleteBehaviorPolicy
        /// </summary>
        public void DeleteBehaviorPolicy(BusinessEntity.BehaviorPolicy request)
        {
            if (request == null)
                throw new ValidationException(933);

            SinacorValidation validation = new SinacorValidation();

            validation.AddValidation<Sinacor.Service.Infra.AuthorizationService.BusinessEntity.BehaviorPolicy>(request, RuleSetConstants.DELETE);

            validation.Validate();

            validation = null;

            ResourceAccess.BehaviorPolicy behaviorPolicyRA = new ResourceAccess.BehaviorPolicy(request.CompanyId.Value);

            behaviorPolicyRA.DeleteBehaviorPolicy(request);

        }

        /// <summary>
        /// Método RetrieveBehaviorPolicy
        /// </summary>
        public BusinessEntity.BehaviorPolicy RetrieveBehaviorPolicy(BusinessEntity.BehaviorPolicy request)
        {
            if (request == null)
                throw new ValidationException(931);

            BusinessEntity.BehaviorPolicy response = new BusinessEntity.BehaviorPolicy();
            SinacorValidation validation = new SinacorValidation();

            validation.AddValidation<Sinacor.Service.Infra.AuthorizationService.BusinessEntity.BehaviorPolicy>(request, RuleSetConstants.SELECTONE);

            validation.Validate();

            validation = null;

            ResourceAccess.BehaviorPolicy behaviorPolicyRA = new ResourceAccess.BehaviorPolicy(request.CompanyId.Value);

            response = behaviorPolicyRA.RetrieveBehaviorPolicy(request);

            return response;
        }

        /// <summary>
        /// Método RetrieveBehaviorPolicies
        /// </summary>
        public List<BusinessEntity.BehaviorPolicy> RetrieveBehaviorPolicies(BusinessEntity.BehaviorPolicy request)
        {
            if (request == null)
                throw new ValidationException(932);

            SinacorValidation validation = new SinacorValidation();

            validation.AddValidation<Sinacor.Service.Infra.AuthorizationService.BusinessEntity.BehaviorPolicy>(request, RuleSetConstants.SELECTMANY);

            validation.Validate();

            validation = null;

            List<BusinessEntity.BehaviorPolicy> response = new List<BusinessEntity.BehaviorPolicy>();

            ResourceAccess.BehaviorPolicy behaviorPolicyRA = new ResourceAccess.BehaviorPolicy(request.CompanyId.Value);

            response = behaviorPolicyRA.RetrieveBehaviorPolicies(request);

            if (response == null)
                response = new List<Sinacor.Service.Infra.AuthorizationService.BusinessEntity.BehaviorPolicy>();

            return response;
        }


        /// <summary>
        /// Método resposável por retornar todas as BehaviorPolicies do usuário autenticado
        /// e gerar as BehaviorPolicy default para as Policy as quais o usuário não tem acesso
        /// Utiliza o contexto do usuario Autenticado
        /// </summary>
        /// <param name="request"></param>
        /// <param name="moduleId"></param>
        /// <returns></returns>
        public List<BusinessEntity.BehaviorPolicy> RetrieveUserBehaviorPolicies(BusinessEntity.BehaviorPolicy request, string moduleId)
        {
            SinacorValidation validation = new SinacorValidation();

            validation.AddValidation<Sinacor.Service.Infra.AuthorizationService.BusinessEntity.BehaviorPolicy>(request, RuleSetConstants.SELECTMANY);

            validation.Validate();

            validation = null;

            if (String.IsNullOrEmpty(moduleId))
            {
                throw new ValidationException(45);
            }
            else
            {
                List<BusinessEntity.BehaviorPolicy> response = new List<BusinessEntity.BehaviorPolicy>();

                //recuperar as roles do usuario de acordo com a company e o modulo passados via parametro
                List<string> roles = AuthorizationContext.Current.RetrieveUserRoles(request.CompanyId.Value, moduleId);

                //criar o resource access de Behavior Policy apontando para a base da company passada como parametro
                ResourceAccess.BehaviorPolicy behaviorPolicyRA = new ResourceAccess.BehaviorPolicy(request.CompanyId.Value);

                //recuperar as BehaviorPolicies do usuario autenticao de acordo com a company passada como parametro
                response = behaviorPolicyRA.RetrieveUserBehaviorPolicies(roles);

                if (response == null)
                    response = new List<Sinacor.Service.Infra.AuthorizationService.BusinessEntity.BehaviorPolicy>();
                else
                {
                    //remover as behavior policies com duplicidade, ou seja policies que estejam 
                    //associadas a mais de uma role a qual o usuário tem acesso
                    Dictionary<int,BusinessEntity.BehaviorPolicy> temp = new Dictionary<int,BusinessEntity.BehaviorPolicy>();

                    foreach(BusinessEntity.BehaviorPolicy item in response)                    
                    {
                        //se ja existe uma policita com o mesmo id, verificar qual a mais permissiva
                        //e manter a mesma
                        if (temp.ContainsKey(item.AssociatedPolicy.PolicyId.Value))
                        {
                            int a, b;

                            //criar numero para indicar o quanto permissiva a politica é 
                            //caso a politica nao permita ver o dado, sao descontados 2 "pontos", para
                            //garatir que caso a politia esteja configura de forma incorreta (permite editar mais nao permite ver)
                            //a mesma ficar como menos permissiva
                            a = (temp[item.AssociatedPolicy.PolicyId.Value].IsVisible.GetValueOrDefault() ? 1 : -2) +
                                (temp[item.AssociatedPolicy.PolicyId.Value].IsEditable.GetValueOrDefault() ? 2 : 0);

                            //criar numero para indicar o quanto permissiva a politica é 
                            //caso a politica nao permita ver o dado, sao descontados 2 "pontos", para
                            //garatir que caso a politia esteja configura de forma incorreta (permite editar mais nao permite ver)
                            //a mesma ficar como menos permissiva
                            b = (item.IsVisible.GetValueOrDefault() ? 1 : -2) +
                                (item.IsEditable.GetValueOrDefault() ? 2 : 0);

                            //se a politica da iteracao, for mais permissiva do que a que ja estiver
                            //adicionada no dicionario de politicas
                            if (b > a)
                            {
                                temp[item.AssociatedPolicy.PolicyId.Value] = item;
                            }
                        }
                        else
                        {
                            temp.Add(item.AssociatedPolicy.PolicyId.Value, item);
                        }
                    }

                    response.Clear();
                    response.AddRange(temp.Values);
                }

                //adicionar as behavior policies as quais o usuario nao tem acesso
                response.AddRange(RetrieveUserNotAssociatedBehaviorPolicies(response, request.CompanyId.Value));

                return response;
            }
        }

        /// <summary>
        /// Retornar as Behavior Policies que não estao associadas ao usuario
        /// Necessario para poder bloquear o acesso a dados os quais o usuário nao tem acesso a Policy
        /// Utiliza o contexto do usuario Autenticado
        /// </summary>
        /// <returns></returns>
        private List<BusinessEntity.BehaviorPolicy> RetrieveUserNotAssociatedBehaviorPolicies(List<BusinessEntity.BehaviorPolicy> userAssociatedBehaviorPolicies, int companyId)
        {
            List<BusinessEntity.BehaviorPolicy> userNotAssociatedBehaviorPolicies = new List<BusinessEntity.BehaviorPolicy>();
            List<BusinessEntity.Policy> userAssociatedPolicies = new List<BusinessEntity.Policy>();
            List<BusinessEntity.Policy> userNotAssociatedPolicies = null;

            if (userAssociatedBehaviorPolicies != null)
            {
                //varrer as berravior policies as quais o usuario tem acesso
                //e extrair as policies as quais o usuario tem acesso
                foreach (BusinessEntity.BehaviorPolicy bp in userAssociatedBehaviorPolicies)
                {
                    userAssociatedPolicies.Add(bp.AssociatedPolicy);
                }

                //recuperar as Policies as quais o usuario nao tem acesso
                userNotAssociatedPolicies = _policyBL.RetrieveUserNotAssociatedPolicies(userAssociatedPolicies);

                //varrear as Policies as quais o usuario nao tem acesso e gerar as BehaviorPolicies default
                //para cada tipo de Policy
                foreach (BusinessEntity.Policy p in userNotAssociatedPolicies)
                {
                    userNotAssociatedBehaviorPolicies.Add(CreateDefaultBehaviorPolicy(p, companyId));
                }
            }

            return userNotAssociatedBehaviorPolicies;
        }

        /// <summary>
        /// Criar uma BehaviorPolicy para uma Policy a qual o usuario não tem acesso
        /// </summary>
        /// <param name="policy"></param>
        /// <returns></returns>
        private BusinessEntity.BehaviorPolicy CreateDefaultBehaviorPolicy(BusinessEntity.Policy policy, int companyId)
        {
            BusinessEntity.BehaviorPolicy ret = new BusinessEntity.BehaviorPolicy();

            //caso a politica associada tenha um valor default para permissao de edicao e permissao de visibilidade
            //assumir o valor padrao
            if (policy.IsEditable.HasValue && policy.IsVisible.HasValue)
            {
                ret.IsEditable = policy.IsEditable.GetValueOrDefault();
                ret.IsVisible = policy.IsVisible.GetValueOrDefault();
            }
            //caso contrario assumir o valor padrao de acordo com o tipo da politica
            else
            {
                //se a politica for permissiva, liberar edicao e visibilidade
                if (policy.PolicyType == BusinessEntity.Enum.PolicyType.Permissive)
                {
                    ret.IsEditable = true;
                    ret.IsVisible = true;
                }
                //se a politica for restritiva, bloquear edicao e visibilidade
                else if (policy.PolicyType == BusinessEntity.Enum.PolicyType.Restrictive)
                {
                    ret.IsEditable = false;
                    ret.IsVisible = false;
                }
            }

            ret.AssociatedPolicy = policy;
            ret.CompanyId = companyId;

            return ret;
        }
        #endregion

    }
}
